﻿using System;

namespace Eventless
{
    public static class Binding
    {    
        public static IDisposable BindTo<T>(this IWriteable<T> first, IWriteable<T> second)
        {
            first.Value = second.Value;

            // ReSharper disable ImplicitlyCapturedClosure
            Action firstHandler = () => second.Value = first.Value;
            Action secondHandler = () => first.Value = second.Value;
            // ReSharper restore ImplicitlyCapturedClosure

            first.Changed += firstHandler;
            second.Changed += secondHandler;

            return new DisposeAction(() =>
                {
                    first.Changed -= firstHandler;
                    second.Changed -= secondHandler;
                });
        }
        
        public static IDisposable BindTo<T>(this IWriteable<T> first, IReadable<T> second)
        {
            // ReSharper disable ImplicitlyCapturedClosure
            first.Value = second.Value;
            Action secondHandler = () => first.Value = second.Value;
            second.Changed += secondHandler;
            return new DisposeAction(() => second.Changed -= secondHandler);
            // ReSharper restore ImplicitlyCapturedClosure
        }

        public static IDisposable BindTo<T>(this IWriteable<T> first, Func<T> second)
        {
            return first.BindTo(Computed.From(second));
        }

        public static void BindTo<TDestination, TSource>(this IWriteableList<TDestination> destination,
                                                         IWriteableList<TSource> source,
                                                         Func<TSource, TDestination> template)
        {
            destination.Clear();

            foreach (var item in source)
                destination.Add(template(item));

            source.Added += index => destination.Insert(index, template(source[index]));
            source.Updated += index => destination[index] = template(source[index]);
            source.Removed += index => destination.RemoveAt(index);
            source.Cleared += () => destination.Clear();
        }
    }
}